package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.msg.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author liuchun
 * @version 1.0
 * @date 2024/9/7 17:18
 */
@Service
public class SignRecordServiceImpl implements ISignRecordService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitMqHelper rabbitMqHelper;

    @Override
    public SignResultVO addSignRecord() {
        //获取用户Id
        Long userId = UserContext.getUser();
        LocalDate now = LocalDate.now();
        String time = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId.toString() + time;
        //签到记录存入Redis
        Boolean success = redisTemplate.opsForValue().setBit(key, now.getDayOfMonth() - 1, true);
        if (success) {
            throw new BizIllegalException("不能重复签到");
        }
        //计算签到积分
        int SignNum = countSignRecord(key,now.getDayOfMonth());
        //签到积分
        int rewardPoints = 0;
        switch (SignNum) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        //准备发送的消息
        SignInMessage msg = new SignInMessage();
        msg.setUserId(userId);
        msg.setPoints(rewardPoints + 1);

        // 保存积分明细记录
        rabbitMqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                 msg
        );

        //封装数据返回
        SignResultVO vo = new SignResultVO();
        vo.setSignDays(SignNum);
        vo.setRewardPoints(rewardPoints);
        return vo;
    }

    /**
     * 查询签到记录
     * @return
     */
    @Override
    public List<Integer> getSignRecord() {
        Long userId = UserContext.getUser();
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();
        String time = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId.toString()+ time;
        List<Long> list = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(list)) {
            return new ArrayList<Integer>(dayOfMonth);
        }
        int offset = dayOfMonth-1;
        int signData = list.get(0).intValue();
        List<Integer> result = new ArrayList<>(dayOfMonth);
        while (offset >= 0) {
            result.add((signData & 1));
            offset--;
            signData >>>= 1;
        }
        //反转一下
        Collections.reverse(result);
        return result;
    }
    //统计连续签到天数
    private int countSignRecord(String key, int dayOfMonth) {
        List<Long> list = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (list == null || list.isEmpty()) {
            return 0;
        }
        int num = list.get(0).intValue();
        int count = 0;
        while ((num & 1) == 1) {
            count ++;
            num = num >>> 1;
        }
        return count;
    }
}
